#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <tcp_socket.h>
#include <tools.h>
#include "bank.h"

// 开户
void create_acc(Msgstoc* stoc,Msgctos* ctos)
{

	char path[PATH_MAX];
	sprintf(path,"%s%s",ACC_PATH,ctos->acc.card);

	//检测身份证是否存在
	if(0 == access(path,F_OK))
	{
		sprintf(stoc->str,"%s 证件已经在本行开户\n",ctos->acc.card);
		return;
	}
	
	//自动生成id
	size_t bank;
	if(get_id(BANK_PATH,&bank))
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		return;
	}

	sprintf(ctos->acc.bank,"%u",bank);
	//debug("自动生成银行卡号:%s\n",ctos->acc.bank);

	// 初始化锁定状态
	ctos->acc.lock = 0;
	
	//存储账户信息
	int fd = open(path,O_WRONLY|O_CREAT,0600);
	if(0 > fd)
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		printf("创建帐户文件失败！\n");
		return;
	}

	write(fd,&ctos->acc,sizeof(ctos->acc));
	close(fd);
	//debug("写入账户信息 %u byte\n",size);
	
	//创建链接文件
	char linkpath[PATH_MAX];
	sprintf(linkpath,"%s%s",ACC_PATH,ctos->acc.bank);
	link(path,linkpath);
	//debug("创建账户的链接文件：%s %s\n",path,linkpath);
	
	stoc->type = M_SUCCESS;
	sprintf(stoc->str,"卡号%s 余额%g\n",ctos->acc.bank,ctos->acc.balance);
	//debug("开户成功！\n");
}

// 销户
void destroy_acc(Msgstoc* stoc,Msgctos* ctos)
{
	char path[PATH_MAX];
	sprintf(path,"%s%s",ACC_PATH,ctos->acc.bank);
	if(0 != access(path,F_OK))
	{
		sprintf(stoc->str,"%s 账号不存在，销户失败\n",
			ctos->acc.bank);
		return;
	}

	int fd = open(path,O_RDONLY);
	if(0 > fd)
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		return;
	}

	Acc acc;
	read(fd,&acc,sizeof(acc));
	close(fd);
	//debug("读取账户信息 %u byte\n",size);
	
	// 检查余额
	if(acc.balance > 0)
	{
		sprintf(stoc->str,"该账户中还有余额%g，销户失败\n",
			acc.balance);
		return;
	}
	
	// 比较姓名
	if(strcmp(acc.name,ctos->acc.name))
	{
		sprintf(stoc->str,"姓名错误，销户失败\n");
		return;
	}
	
	// 比较证件号
	if(strcmp(acc.card,ctos->acc.card))
	{
		sprintf(stoc->str,"证件号错误，销户失败\n");
		return;
	}
	
	// 比较密码
	if(strcmp(acc.passwd,ctos->acc.passwd))
	{
		sprintf(stoc->str,"密码错误，销户失败\n");
		return;
	}
	
	//删除账户文件和链接文件
	char linkpath[PATH_MAX];
	sprintf(linkpath,"%s%s",ACC_PATH,ctos->acc.bank);
	
	unlink(path);
	unlink(linkpath);
	//debug("删除账户文件和链接文件！\n");
	stoc->type = M_SUCCESS;
	sprintf(stoc->str,"账户%s，销户成功！\n",acc.bank);
	//debug("销户成功！\n");
}

// 登录
void login_acc(Msgstoc* stoc,Msgctos* ctos)
{
	char path[PATH_MAX];
	sprintf(path,"%s%s",ACC_PATH,ctos->acc.bank);
	if(0 != access(path,F_OK))
	{
		sprintf(stoc->str,"%s 账号不存在，登录失败\n",
			ctos->acc.bank);
		return;
	}

	int fd = open(path,O_RDWR);
	if(0 > fd)
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		return;
	}

	Acc acc;
	read(fd,&acc,sizeof(acc));
	//debug("读取账户信息 %u byte\n",size);
	
	// 判断账户是否被锁定
	if(acc.lock >= LOCK_CNT)
	{
		sprintf(stoc->str,"该账号已被锁定，登录失败\n");
		return;
	}
	
	// 比较密码
	if(strcmp(acc.passwd,ctos->acc.passwd))
	{
		sprintf(stoc->str,"密码错误，登录失败！\n");
		acc.lock++;
		lseek(fd,0,SEEK_SET);
		write(fd,&acc,sizeof(acc));
		close(fd);
		//debug("记录账户号错误数 %hhd byte\n",acc.lock);
		return;
	}

	stoc->type = M_SUCCESS;
	sprintf(stoc->str,"%s ，登录成功！\n",acc.name);
	//debug("登录成功！\n");
	close(fd);
		
}

void unlock_acc(Msgstoc* stoc,Msgctos* ctos)
{
	char path[PATH_MAX];
	sprintf(path,"%s%s",ACC_PATH,ctos->acc.bank);
	if(0 != access(path,F_OK))
	{
		sprintf(stoc->str,"%s 账号不存在，解锁失败\n",
			ctos->acc.bank);
		return;
	}

	//打开账户
	int fd = open(path,O_RDWR);
	if(0 > fd)
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		return;
	}
	
	//读取账户信息
	Acc acc;
	read(fd,&acc,sizeof(acc));
	//close(fd);
	//debug("读取账户信息 %u byte\n",size);
	
	// 检查余额
	if(acc.balance > 0)
	{
		sprintf(stoc->str,"该账户中还有余额%g，解锁失败\n",
			acc.balance);
		return;
	}
	
	// 比较姓名
	if(strcmp(acc.name,ctos->acc.name))
	{
		sprintf(stoc->str,"姓名错误，解锁失败\n");
		return;
	}
	
	// 比较证件号
	if(strcmp(acc.card,ctos->acc.card))
	{
		sprintf(stoc->str,"证件号错误，解锁失败\n");
		return;
	}
	
	// 比较密码
	if(strcmp(acc.passwd,ctos->acc.passwd))
	{
		sprintf(stoc->str,"密码错误，解锁失败\n");
		return;
	}
	
	acc.lock = 0;
	lseek(fd,0,SEEK_SET);
	write(fd,&acc,sizeof(acc));
	close(fd);

	stoc->type = M_SUCCESS;
	sprintf(stoc->str,"账户%s，解锁成功！\n",acc.bank);
	
}

void save_acc(Msgstoc* stoc,Msgctos* ctos)
{
	char path[PATH_MAX];
	//打开账户
	int fd = open(path,O_RDWR);
	if(0 > fd)
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		return;
	}
	
	//读取账户信息
	Acc acc;
	read(fd,&acc,sizeof(acc));
	//close(fd);
	//debug("读取账户信息 %u byte\n",size);
	
	acc.balance += ctos->acc.balance;
	lseek(fd,0,SEEK_SET);
	write(fd,&acc,sizeof(acc));
	close(fd);

	stoc->type = M_SUCCESS;
	sprintf(stoc->str,"账户%s，存款成功！当前余额%g\n",acc.bank,acc.balance);
	
}

void take_acc(Msgstoc* stoc,Msgctos* ctos)
{
	char path[PATH_MAX];
	//打开账户
	int fd = open(path,O_RDWR);
	if(0 > fd)
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		return;
	}
	
	Acc acc;
	read(fd,&acc,sizeof(acc));

	// 检查余额
	if(acc.balance < ctos->acc.balance)
	{
		sprintf(stoc->str,"余额不足!\n");
		close(fd);
		return;
	}

	
	acc.balance -= ctos->acc.balance;
	lseek(fd,0,SEEK_SET);
	write(fd,&acc,sizeof(acc));
	close(fd);

	stoc->type = M_SUCCESS;
	sprintf(stoc->str,"账户%s，取款成功！当前余额%g\n",acc.bank,acc.balance);
	
}

// 转账
void transf_acc(Msgstoc* stoc,Msgctos* ctos)
{
	char src_path[PATH_MAX];
	sprintf(src_path,"%s%s",ACC_PATH,ctos->acc.bank);
	if(0 != access(src_path,F_OK))
	{
		sprintf(stoc->str,"%s 账号不存在，转账失败\n",
			ctos->acc.bank);
		return;
	}
	
	char dest_path[PATH_MAX];
	sprintf(dest_path,"%s%s",ACC_PATH,ctos->acc.card);
	if(0 != access(dest_path,F_OK))
	{
		sprintf(stoc->str,"%s 账号不存在，转账失败\n",ctos->acc.card);
		return;
	}

	//打开付款者和收款者并读取信息
	int src_fd = open(src_path,O_RDWR);
	int dest_fd = open(dest_path,O_RDWR);
	if(0 > src_fd || 0 > dest_fd)
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		return;
	}

	Acc src_acc,dest_acc;
	read(src_fd,&src_acc,sizeof(src_acc));
	
	read(dest_fd,&dest_acc,sizeof(dest_acc));
	
	// 检查余额
	if(src_acc.balance < ctos->acc.balance)
	{
		sprintf(stoc->str,"该账户中还有余额%g，转账失败\n",src_acc.balance);
		return;
	}
	
	// 转账
	src_acc.balance -= ctos->acc.balance;
	dest_acc.balance += ctos->acc.balance;
	
	//写入金额变化后的账目信息
	lseek(src_fd,0,SEEK_SET);
	write(src_fd,&src_acc,sizeof(src_acc));
	close(src_fd);
	
	lseek(dest_fd,0,SEEK_SET);
	write(dest_fd,&dest_acc,sizeof(dest_acc));
	close(dest_fd);
	stoc->type = M_SUCCESS;
	sprintf(stoc->str,"转账成功！账号:%s 余额:%g",src_acc.bank,src_acc.balance);
}

// 改密码
void modify_acc(Msgstoc* stoc,Msgctos* ctos)
{
	//打开账户并读取信息
	char path[PATH_MAX];
	sprintf(path,"%s%s",ACC_PATH,ctos->acc.bank);
	if(0 != access(path,F_OK))
	{
		sprintf(stoc->str,"%s 账号不存在，改密码失败\n",
			ctos->acc.bank);
		return;
	}

	int fd = open(path,O_RDWR);
	if(0 > fd)
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		printf("打开帐户文件失败！\n");
		return;
	}

	Acc acc;
	read(fd,&acc,sizeof(acc));
	
	// 比较姓名
	if(strcmp(acc.name,ctos->acc.name))
	{
		sprintf(stoc->str,"姓名错误，改密码失败\n");
		close(fd);
		return;
	}
	
	// 比较证件号
	if(strcmp(acc.card,ctos->acc.card))
	{
		sprintf(stoc->str,"证件号错误，改密码失败\n");
		close(fd);
		return;
	}
	
	// 更新密码
	strcpy(acc.passwd,ctos->acc.passwd);
	lseek(fd,0,SEEK_SET);
	write(fd,&acc,sizeof(acc));
	close(fd);
	stoc->type = M_SUCCESS;
	sprintf(stoc->str,"账户%s，改密码成功！\n",acc.bank);
}

void query_acc(Msgstoc* stoc,Msgctos* ctos)
{
	char path[PATH_MAX];
	//打开账户
	int fd = open(path,O_RDWR);
	if(0 > fd)
	{
		sprintf(stoc->str,"服务器正在升级，请稍候再试!\n");
		return;
	}
	
	Acc acc;
	read(fd,&acc,sizeof(acc));
	close(fd);

	stoc->type = M_SUCCESS;
	sprintf(stoc->str,"账户%s，查询成功！当前余额%g\n",acc.bank,acc.balance);
	
}

void* run(void* arg)
{
	TcpSocket* cts = (TcpSocket*)arg;

	Msgctos* ctos = malloc(sizeof(Msgctos));
	Msgstoc* stoc = malloc(sizeof(Msgstoc));

	for(;;)
	{
		if(0 > recv_tcp(cts,ctos,sizeof(*ctos)))
			break;


		stoc->type = M_FAILED;
		switch(ctos->type)
		{
			case M_CREATE:create_acc(stoc,ctos);break;
			case M_DESTROY:destroy_acc(stoc,ctos);break;
			case M_LOGIN:login_acc(stoc,ctos);break;
			case M_UNLOCK:unlock_acc(stoc,ctos);break;
			case M_SAVE:save_acc(stoc,ctos);break;
			case M_TAKE:take_acc(stoc,ctos);break;
			case M_TRANSF:transf_acc(stoc,ctos);break;
			case M_QUERY:query_acc(stoc,ctos);break;
			case M_MODIFY:modify_acc(stoc,ctos);break;
			default:
				stoc->type = -1;
				sprintf(stoc->str,"未知操作\n");
		}
		if(0 >= send_tcp(cts,stoc,sizeof(*stoc)))
			break;
	}
	free(ctos);
	free(stoc);
	close_tcp(cts);
	return 0;
}

int main(int argc,const char* argv[])
{
	if(3 != argc)
	{
		puts("Use:./server <ip> <port>");
		return 0;
	}

	TcpSocket* ts = create_tcp(argv[1],atoi(argv[2]));
	listen_tcp(ts);

	for(;;)
	{
		TcpSocket* cts = accept_tcp(ts);
		if(NULL == cts)
			pthread_exit(NULL);
		
		pthread_t tid;
		pthread_create(&tid,NULL,run,cts);
	}
}
